package game.dungeon;

import game.dungeon.position.Position;
import game.square.SquareImpl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * An abstract class of singular dungeons as a special type of dungeon.
 
 * @version 1.0
 * @author Chris Desomer, Geert Heyman
 */
public abstract class SingularDungeon<S extends SquareImpl> extends Dungeon<S> {

	/**
	 * Initialize a new singular dungeon.
	 * 
	 * @param	boundaryPosition
	 * 			the boundary position for this dungeon
	 * @pre		The given boundary position must be effective.
	 * 		  | boundaryPosition != null
	 * @effect	..
	 * 		  | super(boundaryPosition)
	 * @post	..
	 * 		  | getSquares() != null && getSquares().isEmpty()
	 */
	public SingularDungeon(Position boundaryPosition) {
		super(boundaryPosition);
		squares = new HashMap<Position, S>();
	}
	
	/**
	 * Check if this singular dungeon contains the given position in its
	 * axes.
	 * 
	 * @return	..
	 * 		  | position.isWithinBoundaryOf(getBoundaryPosition())
	 */
	@Override
	public boolean containsPosition(Position position) {
		return position.isWithinBoundaryOf(getBoundaryPosition());
	}

	/**
	 * Return a copy of the squares of this singular dungeon.
	 */
	@Override
	public Map<Position, S> getSquares() {
		return new HashMap<Position,S>(squares);
	}
	/**
	 * Get a set containing only this singular dungeon.
	 * 
	 * @return	..
	 * 		  | result.contains(this) && result.size() == 1
	 */
	@Override
	public Set<SingularDungeon<? extends S>> getSingularDungeons(){
		Set<SingularDungeon<? extends S>> singularDungeons = new HashSet<SingularDungeon<? extends S>>();
		singularDungeons.add(this);
		return singularDungeons;
	}
	
	/**
	 * Add a square to this dungeon at the given position.
	 * 
	 * @param 	square
	 * 			The square to add.
	 * @param 	position
	 * 			The position where the square should be added.
	 * @pre 	..
	 * 		  | canHaveAsSquareAtPosition(square,position)
	 * @pre		..	
	 * 		  |	square.getNbOfDungeons() == 0
	 * @pre		The given square isn't slippery or the percentage of slippery squares
	 * 			if the given square is added isn't higher than 20%
	 * @post	..
	 * 		  |	new.getSquares().contains(square)
	 * @effect	..
	 * 		  |	square.incrementNbOfDungeons()
	 */
	@Override
	public void addSquareAtPosition(S square,Position position) {
		super.addSquareAtPosition(square, position);
		squares.put(position, square);
		square.incrementNbOfDungeons();

	}
	
	/**
	 * Remove the given square from the squares of this dungeons.
	 * 
	 * @param 	square
	 * 			The square to remove.
	 * @pre		..
	 * 		  | square != null
	 * @pre		..
	 * 		  |	getSquares().containsValue(square)
	 * @post	..
	 * 		  | !new.getSquares().containsValue(square)
	 * @post	If the given square is on of the squares in this dungeon
	 * 			the given square is terminated.
	 */
	@Override
	public void removeSquare(SquareImpl square){
		for (Position position : squares.keySet())
			if (squares.get(position) == square){
				squares.remove(position);
				square.terminate();
			}
	}

	/**
	 * Variable referencing a map collecting all squares in this
	 * dungeon.
	 */
	private Map<Position, S> squares;
	
	/**
	 * Return an iterator object with all the squares in this singular
	 * dungeon.
	 * 
	 * @return	..
	 * 		  | getSquares().values().iterator()
	 */
	@Override
	public Iterator<S> iterator() {
        return squares.values().iterator();
    }
}
